package org.example.business_logic.action;

import org.example.business_logic.component.Ball;
import org.example.business_logic.component.StraightOrbit;
import org.example.physics_engine.Epsilon;
import org.example.physics_engine.collision.CollisionFilter;
import org.example.physics_engine.collision.manifold.Penetration;
import org.example.physics_engine.body.PhysicsBody;
import org.example.physics_engine.geometry.Vector2d;
import org.example.physics_engine.geometry.shapes.AbstractShape;

import static org.example.business_logic.Settings.ORBIT_PIERCE_BIAS;

public class StraightOrbitAction implements CollisionFilter {
    private final Vector2d gravity;

    private double ballX;
    private double ballY;

    private double minX;
    private double maxX;
    private double minY;
    private double maxY;
    private double radius;
    private StraightOrbit.OrbitDirection orbitDirection;

    private Vector2d linearVelocity;

    public StraightOrbitAction(Vector2d gravity){
        this.gravity=gravity;
    }

    @Override
    public boolean isAllowedBroadPhase(PhysicsBody body1, PhysicsBody body2) {
        return true;
    }

    @Override
    public boolean isAllowedNarrowPhase(PhysicsBody body1, PhysicsBody body2) {
        return true;
    }

    @Override
    public boolean isAllowedManifold(PhysicsBody body1, PhysicsBody body2, AbstractShape shape, Penetration penetration) {
        AbstractShape shape1=body1.getShape();
        AbstractShape shape2=body2.getShape();

        if(!(shape2 instanceof StraightOrbit)){
            return true;
        }
        Ball ball=(Ball) shape1;
        StraightOrbit orbit=(StraightOrbit) shape2;

        double orbitX=orbit.getTransform().getX();
        double orbitY=orbit.getTransform().getY();

        ballX=ball.getTransform().getX();
        ballY=ball.getTransform().getY();
        radius=ball.getRadius();
        orbitDirection=orbit.getOrbitDirection();

        minX=orbitX-orbit.getHalfWidth();
        maxX=orbitX+orbit.getHalfWidth();
        minY=orbitY-orbit.getHalfHeight();
        maxY=orbitY+orbit.getHalfHeight();

        linearVelocity = body1.getLinearVelocity();

        //防止大圆进入小管道
        if (radius > orbit.getHalfHeight()) {
            return true;
        }
        //圆心在管道两侧，直接弹飞
        if (isOutOrbit()) {
            return true;
        }
        //是否与管壁发生碰撞
        if (!isCollision()) {
            // 没有与管壁发生碰撞并且在轨道内，需要反转重力
            if (isInOrbit()) {
                maintainOrbitProperty(body1, body2);
            }
            return false;
        }
        if (!isInOrbit()) {
            if (penetration.getPenetrationNormal().dotProduct(orbit.getNormalVectors()[0]) < 1e5 * Epsilon.E || penetration.getPenetrationNormal().dotProduct(orbit.getNormalVectors()[1]) < 1e5 * Epsilon.E) {
                return false;
            }
            return true;
        }
        //与管壁发生碰撞
        maintainOrbitProperty(body1, body2);
        solveCollision(ball, orbit, penetration);

        return true;
    }

    private boolean isInOrbit(){
        return ballX>minX && ballX<maxX && ballY < maxY && ballY > minY;
    }

    //解决小球和管道的碰撞
    private void solveCollision(Ball ball, StraightOrbit orbit, Penetration penetration) {
        Vector2d normal = penetration.getPenetrationNormal();
        if (orbitDirection == StraightOrbit.OrbitDirection.HORIZONTAL) {
            boolean isHigh = ball.getTransform().getY() > orbit.getTransform().getY();
            //球比orbit高
            if (isHigh) {
                penetration.setDepth(radius - (maxY - ball.getTransform().getY()));//设置穿透深度
                normal.x = 0;
                normal.y = 1;
            } else {
                penetration.setDepth(radius - (ball.getTransform().getY() - minY));
                normal.x = 0;
                normal.y = -1;
            }
        } else {
            boolean isRight = ball.getTransform().getX() > orbit.getTransform().getX();//是否在右边
            if (isRight) {
                penetration.setDepth(radius - (maxX - ball.getTransform().getX()));
                normal.x = 1;
                normal.y = 0;
            } else {
                penetration.setDepth(radius - (ball.getTransform().getX() - minX));
                normal.x = -1;
                normal.y = 0;
            }
        }
    }

    //是否在管道外面
    private boolean isOutOrbit() {
        //横着的管道
        if (orbitDirection == StraightOrbit.OrbitDirection.HORIZONTAL) {
            if (ballY > maxY || ballY < minY) {
                return true;
            }

            if (ballX < minX) {
                Vector2d v1 = new Vector2d(minX - ballX, maxY - ballY);
                Vector2d v2 = new Vector2d(minX - ballX, minY - ballY);
                if (v1.getMagnitude() + ORBIT_PIERCE_BIAS < radius || v2.getMagnitude() + ORBIT_PIERCE_BIAS < radius) {
                    return true;
                }
            } else if (ballX > maxX) {
                Vector2d v1 = new Vector2d(maxX - ballX, maxY - ballY);
                Vector2d v2 = new Vector2d(maxX - ballX, minY - ballY);
                if (v1.getMagnitude() + ORBIT_PIERCE_BIAS < radius || v2.getMagnitude() + ORBIT_PIERCE_BIAS < radius) {
                    return true;
                }
            }
        }
        else {//竖着的管道
            if (ballX > maxX || ballX < minX) {
                return true;
            }

            if (ballY < minY) {
                Vector2d v1 = new Vector2d(minX - ballX, minY - ballY);
                Vector2d v2 = new Vector2d(maxX - ballX, minY - ballY);
                if (v1.getMagnitude() + ORBIT_PIERCE_BIAS < radius || v2.getMagnitude() + ORBIT_PIERCE_BIAS < radius) {
                    return true;
                }
            } else if (ballY > maxY) {
                Vector2d v1 = new Vector2d(minX - ballX, maxY - ballY);
                Vector2d v2 = new Vector2d(maxX - ballX, maxY - ballY);
                if (v1.getMagnitude() +ORBIT_PIERCE_BIAS < radius || v2.getMagnitude() + ORBIT_PIERCE_BIAS < radius) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean isCollision() {
        if (orbitDirection == StraightOrbit.OrbitDirection.HORIZONTAL) {
            return ballY + radius > maxY || ballY - radius < minY;
        } else {
            return ballX + radius > maxX || ballX - radius < minX;
        }
    }

    private void maintainOrbitProperty(PhysicsBody body1, PhysicsBody body2) {
        body1.getForces().clear();
        body1.integrateVelocity(gravity.getNegative());
        if (body1.getShape().getRate() == body2.getShape().getRate()) {
            if (orbitDirection == StraightOrbit.OrbitDirection.HORIZONTAL) {
                linearVelocity.y = 0;
            } else if (orbitDirection ==StraightOrbit.OrbitDirection.VERTICAL) {
                linearVelocity.x = 0;
            }
        }
        if (linearVelocity.getMagnitude() < 30) {
            linearVelocity.multiplyNum(30 / linearVelocity.getMagnitude());
        }
    }
}

